/*!
 * \file Thread.cpp
 * \brief Thread class for Nude.
 * \author Nus
 * \date 2008/03/12, 00:11
 */

#include "Debug.h"
#include "Thread.h"

// Default constructor.
Thread::Thread(u32 stack_size, Allocator* alloc)
    : mStackSize(stack_size),
      mpStack(NULL),
      mState(ST_UNINITIALIZE),
      mTerminated(true)
{
  if(mStackSize < PTHREAD_STACK_MIN)
    mStackSize = PTHREAD_STACK_MIN;

  if(alloc)
    mpStack = alloc->memAlloc(mStackSize, 32, __FILE__, __LINE__);
  else
    mpStack = memAlloc(mStackSize, 32, __FILE__, __LINE__);
}

// Default destructor.
Thread::~Thread()
{
  AutoMutex mtx(&mMutex);
  pthread_attr_destroy(&mThreadAttr);
  if(mpStack) {
    memFree(mpStack);
    mpStack = NULL;
  }
}

// Create thread.
s32 Thread::createThread(void)
{
  if(mState == ST_UNINITIALIZE) {
    AutoMutex mtx(&mMutex);

    s32 ret = 0;

    // Initialize attribute.
    ret = pthread_attr_init(&mThreadAttr);
    if(ret)
      return ret;

    // Set stack size.
    ret = pthread_attr_setstacksize(&mThreadAttr, mStackSize);
    if(ret)
      return ret;

    // Set stack.
    pthread_attr_setstackaddr(&mThreadAttr, mpStack);
    if(ret)
      return ret;

    // Create thread immediately.
    ret = pthread_create(&mThread, &mThreadAttr, &Thread::threadFunc, this);
    if(ret)
      return ret;
    
    mState = ST_RUNNING;
    return EC_NONE;
  }
  
  return EC_CRITICAL_ERROR;
}

// Terminate thread.
s32 Thread::terminateThread(bool non_blocking)
{
  AutoMutex mtx(&mMutex);
  mTerminated = true;
  return EC_NONE;
}

// Suspend thread.
s32 Thread::suspendThread(void)
{
  return EC_NONE;
}

// Initialization procedure.
void Thread::init(void)
{
  AutoMutex mtx(&mMutex);
  mState = ST_INITIALIZING;
}

// Run procedure.
void Thread::run(void)
{
  AutoMutex mtx(&mMutex);
  mState = ST_RUNNING;
}

// Termination procedure.
void Thread::term(void)
{
  AutoMutex mtx(&mMutex);
  mState = ST_TERMINATING;
}

// Thread function.
void* Thread::threadFunc(void* user_param)
{
  Thread* ptr = static_cast< Thread* >(user_param);
  ASSERT_C(ptr != NULL);
  ptr->init();
  ptr->run();
  ptr->term();
  return user_param;
}
